/*
 * Copyright 2010 Michael Nitsche
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.mnit.basis.daten.struktur.tabelle.typ;

import java.util.Iterator;

import de.mnit.basis.daten.struktur.S_LM_Basis;
import de.mnit.basis.daten.struktur.ordnen.Sortieren;
import de.mnit.basis.fehler.Fehler;
import de.mnit.basis.sys.Ausgabe;

public abstract class A_TypTabelle<TG, TART extends S_LM_Basis<?>> implements S_TypTabelle<TG, TART> {

	private Class<?>[] klassen;


	protected A_TypTabelle(Class<?>... klassen) {
		this.klassen=klassen;
	}


	// LESEN

	public abstract int gBreite();

	public abstract TG gZeile(int zeile);

	public abstract TART gSpalte(int spalte);

	public abstract <T> TART gSpalte(int spalte, Class<T> typ);

	public Object gib(int spalte, int zeile) {
		return this.gSpalte(spalte).gib(zeile);
	}

	public TG gib(int zeile) {
		return this.gZeile(zeile);
	}

	public Iterator<TG> iterator() {
		final A_TypTabelle<TG,?> tabelle=this;
		return new Iterator<TG>(){

			private int naechstezeile=1;

			public boolean hasNext() {
				return naechstezeile<=tabelle.gLaenge();
			}

			public TG next() {
				Fehler.zahl.wenn_ZuGross(tabelle.gLaenge(),naechstezeile);
				return tabelle.gZeile(naechstezeile++);
			}

			public void remove() {
				Fehler.sonstige.da_Untypisch("Funktion ungültig!");
			}

		};
	}

	public void ausgeben() {
		Ausgabe.debugVersetzt(1, this);
	}

	public String zuTextDebug() {
		return this.toString();
	}

	public S_TypTabelle<TG, TART> kopie() {
//		Class<?> ca=Object.class;
//		S_TypTabelle erg=TypTabelle.neu(this.typen.zuArray(ca.getClass()));
//		for(Object[] zeile : this) erg.plus(zeile);
//		return erg;
		throw Fehler.sonstige.da_ToDo();
	}

	public int gLaenge() {
		return this.gSpalte(1).gLaenge();
	}

	public boolean istLeer() {
		return gLaenge()==0;
	}


	// AENDERN

	public void sortieren(int... nach) {
		Sortieren.sortieren(this, nach);
	}

	public void setze(int spalte, int zeile, Object objekt) {
		Fehler.zahl.wenn_Ausserhalb(1,gLaenge(),zeile);
		Fehler.zahl.wenn_Ausserhalb(1,gBreite(),spalte);
		istGueltig(spalte,objekt);
		pSetze(spalte, zeile, objekt);
	}

	public void ersetzen(int zeile, Object... objekte) {
		Fehler.zahl.wenn_Ausserhalb(zeile,gLaenge());
		Fehler.zahl.wenn_InListe(zeile,0);
		for(int s=1; s<=gBreite(); s++)
			istGueltig(s,objekte[s-1]);
		pErsetzen(zeile,objekte);
	}

	public void vertauschen(int zeile_a, int zeile_b) {
		Fehler.zahl.wenn_InListe(zeile_a,0);
		Fehler.zahl.wenn_InListe(zeile_b,0);
		Fehler.zahl.wenn_Ausserhalb(1,gLaenge(), Math.abs(zeile_a));
		Fehler.zahl.wenn_Ausserhalb(1,gLaenge(), Math.abs(zeile_b));

		if( zeile_a == zeile_b ) {
			Ausgabe.warnung("Vertauschen von "+(zeile_a)+" und "+(zeile_b)+" nicht nötig!");
			return;
		}

		pVertauschen(zeile_a, zeile_b);
	}

	public void plus(Object... zeile) {
		Fehler.zahl.wenn_Nicht(gBreite(),zeile.length);
		for(int i=1; i<=gBreite(); i++)
			istGueltig(i, zeile[i-1]);
		pPlus(zeile);
	}

	public void einfuegen(int zeile, Object... objekte) {
		Fehler.zahl.wenn_Nicht(gBreite(),objekte.length);
		Fehler.zahl.wenn_Gleich(zeile,0);
		for(int i=1; i<=gBreite(); i++)
			istGueltig(i, objekte[i-1]);
		pEinfuegen(zeile,objekte);
	}

	public TG minus(int pos) {
		TG zeile=this.gZeile(pos);
		pMinus(pos);
		return zeile;
	}

	public TG minusLetzten() {
		int laenge=this.gLaenge();
		TG zeile=this.gZeile(laenge);
		pMinus(laenge);
		return zeile;
	}

	public String toString() {
		StringBuffer sb=new StringBuffer();
		for(int zeile=1; zeile<=gLaenge(); zeile++) {
			for(int spalte=1; spalte<=gBreite(); spalte++)
				sb.append(gib(spalte,zeile) + (spalte!=gBreite() ? "," : ""));
			sb.append("\n");
		}
		return sb.toString();

//		TextDesigner td=new TextDesigner();
//		for(int zeile=1; zeile<=gLaenge(); zeile++) {
//			TD_Zeile z=td.nZeile();
//			for(int spalte=1; spalte<=gBreite(); spalte++)
//				z.l(Wandle.objekt.zuTextDebug(gib(spalte,zeile))).f();
//		}
//		return td.gib();
	}

	public void sortiereSo(int[] so) {
		Sortieren.sortieren(this, so);
	}

	// INTERN

	protected abstract void pPlus(Object[] zeile);

	protected abstract void pEinfuegen(int zeile, Object[] objekte);

	protected abstract void pErsetzen(int zeile, Object[] objekt);

	protected abstract void pVertauschen(int zeile_a, int zeile_b);

	protected abstract void pSetze(int spalte, int zeile, Object objekt);

	protected abstract void pMinus(int zeile);

	protected void istGueltig(int spalte, Object o) {
		if(o!=null && !klassen[spalte-1].isAssignableFrom(o.getClass()))
			Fehler.objekt.da_KlasseUngueltig(o);
	}

}
